package com.ifp.opengate.boot.controller;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;

import com.ifp.core.log.Trace;
import com.ifp.opengate.boot.emum.ErrorEnum;
import com.ifp.opengate.boot.exception.AccessControlException;
import com.ifp.opengate.boot.utils.MailServiceUtils;
import com.ifp.opengate.boot.utils.RequestUtil;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.exception.HystrixTimeoutException;

import reactor.core.publisher.Mono;

/**
 * 项目名：ifp-gateway <br>
 * 包名：com.ifp.opengate.boot.controller <br>
 * 类名：FallbackController <br>
 * 描述：
 *
 * @author Suremotoo
 * @create 2021-06-09 17:33
 */
@RestController
public class FallbackController {

    @Autowired
    private MailServiceUtils mailServiceUtils;

    @PostMapping(value = "/fallback")
    @ResponseStatus
    @HystrixCommand
    public Map<String, Object> fallback(ServerWebExchange exchange) {
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        Exception exception = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);
        ServerWebExchange delegate = ((ServerWebExchangeDecorator)exchange).getDelegate();
        Trace.logError(Trace.MODULE_ACTION, "进入 fallback, 接口调用失败，URL={}", delegate.getRequest().getURI(), exception);

        String actionId = RequestUtil.getActionId(delegate.getRequest());
        String hystrixName = actionId.replaceAll("/", "").replaceAll("\\.", "");

        HystrixCircuitBreaker hystrixCircuitBreaker =
            HystrixCircuitBreaker.Factory.getInstance(HystrixCommandKey.Factory.asKey(hystrixName));
        Trace.logInfo(Trace.MODULE_COMMON, "熔断器 {} 状态 {} ", hystrixName,
            hystrixCircuitBreaker != null ? !hystrixCircuitBreaker.allowRequest() : "null");
        if (hystrixCircuitBreaker == null || hystrixCircuitBreaker.allowRequest()) {
            return getResponseData(exchange, exception);
            // DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(
            // JSONObject.wrap(getResponseData(exchange, exception)).toString().getBytes(StandardCharsets.UTF_8));
            // return exchange.getResponse().writeWith(Mono.just(buffer));
        }
        Trace.logInfo(Trace.MODULE_COMMON, "fallback {} 后续", hystrixName);
        // 发送熔断警告
        mailServiceUtils.sendMailForHystrix(route);

        // 熔断异常
        exception =
            new AccessControlException(ErrorEnum.hystrixFallBackError.code(), ErrorEnum.hystrixFallBackError.msg());
        return getResponseData(exchange, exception);
        // DataBuffer buffer = exchange.getResponse().bufferFactory()
        // .wrap(JSONObject.wrap(getResponseData(exchange, exception)).toString().getBytes(StandardCharsets.UTF_8));
        // return exchange.getResponse().writeWith(Mono.just(buffer));

    }

    private Map<String, Object> getResponseData(ServerWebExchange exchange, Throwable exception) {
        DefaultErrorAttributes defaultErrorAttributes = new DefaultErrorAttributes();
        defaultErrorAttributes.storeErrorInformation(exception, exchange);
        ServerRequest serverRequest = ServerRequest.create(exchange.mutate().request(exchange.getRequest()).build(),
            HandlerStrategies.withDefaults().messageReaders());
        Trace.logInfo(Trace.MODULE_COMMON, "ServerRequest", serverRequest);
        Map<String, Object> errorAttributes = defaultErrorAttributes.getErrorAttributes(serverRequest, false);
        errorAttributes.put("path",
            ((ServerWebExchangeDecorator)exchange).getDelegate().getRequest().getPath().value());
        return errorAttributes;
    }

    @RequestMapping(value = "/fallbackBak")
    @ResponseStatus
    public Mono<Map<String, Object>> fallbackTwo(ServerWebExchange exchange) {
        Map<String, Object> result = new HashMap<>(4);
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

        Exception exception = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);
        ServerWebExchange delegate = ((ServerWebExchangeDecorator)exchange).getDelegate();
        Trace.logError(Trace.MODULE_ACTION, "接口调用失败，URL={}", delegate.getRequest().getURI(), exception);
        if (exception instanceof HystrixTimeoutException) {
            result.put("errorMsg", "接口调用超时: " + exception.getMessage());
        } else if (exception != null && exception.getMessage() != null) {
            result.put("errorMsg", "接口调用失败: " + exception.getMessage());
        } else {
            result.put("errorMsg", "接口调用失败");
        }

        // 发送熔断警告
        mailServiceUtils.sendMailForHystrix(route);

        result.put("errorCode", ErrorEnum.hystrixCircuitError.code());
        result.put("errorMsg", ErrorEnum.hystrixCircuitError.msg());
        return Mono.just(result);
    }

}
